%include
{
/*--------------------------------*- C++ -*----------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | www.openfoam.com
     \\/     M anipulation  |
-------------------------------------------------------------------------------
    Copyright (C) 2019 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

    OpenFOAM is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.

Description
    Lemon grammar for patch expressions.

    https://www.sqlite.org/src/doc/trunk/doc/lemon.html

    See detailed notes in the field expression parser.

\*---------------------------------------------------------------------------*/
} // %include

/*
 * include patchExprLemonParserMacros.m4
 *include(`patchExprLemonParserMacros.m4')dnl
 !done in a comment since many editors have issues matching m4 quotes!
 */
%include
{
#include "patchExprDriver.H"
#include "patchExprParser.H"
#include "patchExprScanner.H"
#include "unitConversion.H"
#include "volFields.H"
#include "error.H"
#include "IOmanip.H"
#include "exprOps.H"
#include "exprDriverOps.H"
#include "GeometricFieldOps.H"

// Enable ParseTrace
#undef NDEBUG

compiler_pragmas()

// Local Functions

tmp_management()

} // %include

// ------------------------------------------------------------------------- //

%namespace      {}

// Use extra argument for the return value
%extra_context  { Foam::expressions::patchExpr::parseDriver* driver }
%parse_failure  { driver->reportFatal("Parse failure, giving up..."); }
%syntax_error   { driver->reportFatal("Syntax error"); }

%token_prefix TOK_

// Terminals
%token_type    {Foam::expressions::patchExpr::scanToken*}
// Non-terminals
%type ivalue   { Foam::label }
%type svalue   { Foam::scalar }
%type ident    { Foam::word* }

// Face fields
declare_field(lfield, Foam::boolField, bool, newField, getSurfaceField)
declare_field(sfield, Foam::scalarField, Foam::scalar, newField, getField)
declare_field(vfield, Foam::vectorField, Foam::vector, newField, getField)
declare_field(hfield, Foam::sphericalTensorField, Foam::sphericalTensor, newField, getField)
declare_field(yfield, Foam::symmTensorField, Foam::symmTensor, newField, getField)
declare_field(tfield, Foam::tensorField, Foam::tensor, newField, getField)

// Point fields
declare_field(plfield, Foam::boolField, bool, newPointField, getPointField)
declare_field(psfield, Foam::scalarField, Foam::scalar, newPointField, getPointField)
declare_field(pvfield, Foam::vectorField, Foam::vector, newPointField, getPointField)
declare_field(phfield, Foam::sphericalTensorField, Foam::sphericalTensor, newPointField, getPointField)
declare_field(pyfield, Foam::symmTensorField, Foam::symmTensor, newPointField, getPointField)
declare_field(ptfield, Foam::tensorField, Foam::tensor, newPointField, getPointField)


// For each rule action with code, destruction must be done by that code block
// Lemon does not generate a destructor for that.
// So do not use Lemon destructors for anything.

operator_precedence()

%start_symbol evaluate

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

/*---------------------------------------------------------------------------*\
 * Productions (scalar)
\*---------------------------------------------------------------------------*/

svalue (lhs) ::= NUMBER (a) .       { lhs = (a)->svalue; }  // From scanToken
svalue (lhs) ::= ZERO .             { lhs = Foam::Zero; }
svalue (lhs) ::= PI LPAREN RPAREN . { lhs = Foam::constant::mathematical::pi; }
svalue (lhs) ::= DEG_TO_RAD LPAREN RPAREN . { lhs = Foam::degToRad(); }
svalue (lhs) ::= RAD_TO_DEG LPAREN RPAREN . { lhs = Foam::radToDeg(); }
svalue (lhs) ::= ARG LPAREN RPAREN .  { lhs = driver->argValue(); }
svalue (lhs) ::= TIME LPAREN RPAREN . { lhs = driver->timeValue(); }


/* * * * * * * * * * * * * * * * * Face Fields * * * * * * * * * * * * * * * *\
dnl
define([_logic_],       [lfield])dnl
define([_scalar_],      [sfield])dnl
define([_vector_],      [vfield])dnl
define([_sphTensor_],   [hfield])dnl
define([_symTensor_],   [yfield])dnl
define([_tensor_],      [tfield])dnl
dnl
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*---------------------------------------------------------------------------*\
 * Productions (scalarField)
dnl
define([_target_],      [sfield])dnl
define([_value_type_],  [Foam::scalar])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a); }

rule_field_from_value(_target_, svalue)
rule_get_field(_target_, SCALAR_ID)
rule_get_field(_target_, SSCALAR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_scalar_operations()
rules_scalar_functions()

// Non-standard but manage via FieldOps::assign
rule_unary_assign(_target_, _target_, FLOOR, Foam::floorOp<Foam::scalar>())
rule_unary_assign(_target_, _target_, CEIL, Foam::ceilOp<Foam::scalar>())
rule_unary_assign(_target_, _target_, ROUND, Foam::roundOp<Foam::scalar>())

// Non-standard but works directly for scalarField
rule_binary_func(_target_, _target_, _target_, HYPOT, Foam::hypot)


// Other functions

_target_ (lhs) ::= RAND LPAREN RPAREN.
{
    lhs = driver->field_rand().ptr();
}

_target_ (lhs) ::= RAND LPAREN NUMBER (seed) RPAREN.
{
    // Call with -ve seed to signal use of time index as seed
    lhs = driver->field_rand(std::round(-(seed)->svalue)).ptr();
}


/*---------------------------------------------------------------------------*\
 * Productions (vectorField)
dnl
define([_target_],      [vfield])dnl
define([_value_type_],  [Foam::vector])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a); }

rule_get_field(_target_, VECTOR_ID)
rule_get_field(_target_, SVECTOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_vector_operations()
rules_vector_functions()


/*---------------------------------------------------------------------------*\
 * Productions (sphericalTensorField)
dnl
define([_target_],      [hfield])dnl
define([_value_type_],  [Foam::sphericalTensor])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a); }

rule_get_field(_target_, SPH_TENSOR_ID)
rule_get_field(_target_, SSPH_TENSOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_sphTensor_operations()
rules_sphTensor_functions()


/*---------------------------------------------------------------------------*\
 * Productions (symmTensorField)
dnl
define([_target_],      [yfield])dnl
define([_value_type_],  [Foam::symmTensor])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a); }

rule_get_field(_target_, SYM_TENSOR_ID)
rule_get_field(_target_, SSYM_TENSOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_symTensor_operations()
rules_symTensor_functions()


/*---------------------------------------------------------------------------*\
 * Productions (tensorField)
dnl
define([_target_],      [tfield])dnl
define([_value_type_],  [Foam::tensor])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a); }
tfield (lhs) ::= UNIT_TENSOR .  { lhs = _new_tfield(Foam::tensor::I); }

rule_get_field(_target_, TENSOR_ID)
rule_get_field(_target_, STENSOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_tensor_operations()
rules_tensor_functions()


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

/*---------------------------------------------------------------------------*\
 * Logic field productions (boolField)
dnl
define([_target_],      [lfield])dnl
define([_value_type_],  [bool])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a); }
_logic_ (lhs) ::= LTRUE .   { lhs = _new_lfield(_logic_true_); }
_logic_ (lhs) ::= LFALSE .  { lhs = _new_lfield(_logic_false_); }

rule_cast_logical(_target_, _target_)
rule_cast_logical(_target_, _scalar_, Foam::scalar)

dnl/* Handling of named logic fields not really tested (disable in scanner) */
rule_get_field(_target_, SBOOL_ID)
rules_logical_operations(_logic_, _value_type_)


/*---------------------------------------------------------------------------*\
 * General Surface-related productions
\*---------------------------------------------------------------------------*/

rules_driver_surface_functions()


/* * * * * * * * * * * * * * * * Point Fields  * * * * * * * * * * * * * * * *\
dnl
define([_logic_],       [plfield])dnl
define([_scalar_],      [psfield])dnl
define([_vector_],      [pvfield])dnl
define([_sphTensor_],   [phfield])dnl
define([_symTensor_],   [pyfield])dnl
define([_tensor_],      [ptfield])dnl
dnl
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


/*---------------------------------------------------------------------------*\
 * Productions (point scalarField)
dnl
define([_target_],      [psfield])dnl
define([_value_type_],  [Foam::scalar])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }

rule_field_from_value(_target_, svalue, POINT_EXPR)
rule_get_field(_target_, PSCALAR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_scalar_operations()
rules_scalar_functions()

// Non-standard but manage via FieldOps::assign
rule_unary_assign(_target_, _target_, FLOOR, Foam::floorOp<Foam::scalar>())
rule_unary_assign(_target_, _target_, CEIL, Foam::ceilOp<Foam::scalar>())
rule_unary_assign(_target_, _target_, ROUND, Foam::roundOp<Foam::scalar>())

// Non-standard but works directly for scalarField
rule_binary_func(_target_, _target_, _target_, HYPOT, Foam::hypot)


/*---------------------------------------------------------------------------*\
 * Productions (point vectorField)
dnl
define([_target_],      [pvfield])dnl
define([_value_type_],  [Foam::vector])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }

rule_get_field(_target_, PVECTOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_vector_operations()
rules_vector_functions()


/*---------------------------------------------------------------------------*\
 * Productions (point sphericalTensorField)
dnl
define([_target_],      [phfield])dnl
define([_value_type_],  [Foam::sphericalTensor])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }

rule_get_field(_target_, PSPH_TENSOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_sphTensor_operations()
rules_sphTensor_functions()


/*---------------------------------------------------------------------------*\
 * Productions (point symmTensorField)
dnl
define([_target_],      [pyfield])dnl
define([_value_type_],  [Foam::symmTensor])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }

rule_get_field(_target_, PSYM_TENSOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_symTensor_operations()
rules_symTensor_functions()


/*---------------------------------------------------------------------------*\
 * Productions (point tensorField)
dnl
define([_target_],      [ptfield])dnl
define([_value_type_],  [Foam::tensor])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }

rule_get_field(_target_, PTENSOR_ID)

rules_standard(_target_, _value_type_, _logic_)
rules_inplace_gUnary(_target_)
rules_tensor_operations()
rules_tensor_functions()


/*---------------------------------------------------------------------------*\
 * Logic field productions (point boolField)
dnl
define([_target_],      [plfield])dnl
define([_value_type_],  [bool])dnl
dnl
\*---------------------------------------------------------------------------*/

evaluate ::= _target_ (a) . { driver->setResult(a, true); /* Point */ }
_logic_ (lhs) ::= POINT_EXPR LPAREN LTRUE RPAREN .  { lhs = _new_plfield(_logic_true_); }
_logic_ (lhs) ::= POINT_EXPR LPAREN LFALSE RPAREN . { lhs = _new_plfield(_logic_false_); }

rule_cast_logical(_target_, _target_)
rule_cast_logical(_target_, _scalar_, Foam::scalar)

dnl/* Handling of named logic fields not really tested (disable in scanner) */
rule_get_field(_target_, PBOOL_ID)
rules_logical_operations(_logic_, _value_type_)


/*---------------------------------------------------------------------------*\
 * General Point-related productions
\*---------------------------------------------------------------------------*/

rules_driver_point_functions()


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

/*---------------------------------------------------------------------------*\
 * Face field composition
\*---------------------------------------------------------------------------*/

rule_mag_logical(sfield, lfield)
rules_mag_functions(sfield, sfield)
rules_mag_functions(sfield, vfield)
rules_mag_functions(sfield, tfield)
rules_mag_functions(sfield, yfield)
rules_mag_functions(sfield, hfield)

rule_vector_zip(vfield, sfield, VECTOR)
rule_tensor_zip(tfield, sfield, TENSOR)
rule_symTensor_zip(yfield, sfield, SYM_TENSOR)
rule_sphTensor_zip(hfield, sfield, SPH_TENSOR)

rule_vector_components(sfield, vfield)
rule_tensor_components(sfield, tfield)
rule_symTensor_components(sfield, yfield)
rule_sphTensor_components(sfield, hfield)

rule_tensor_transpose(tfield)
rule_symTensor_transpose(yfield)
rule_sphTensor_transpose(hfield)

rule_tensor_unzipDiag(vfield, yfield)
rule_tensor_unzipAll(vfield, tfield)

rule_pointToFace(sfield, psfield)
rule_pointToFace(vfield, pvfield)
rule_pointToFace(tfield, ptfield)
rule_pointToFace(yfield, pyfield)
rule_pointToFace(hfield, phfield)


/*---------------------------------------------------------------------------*\
 * Point field composition
\*---------------------------------------------------------------------------*/

rule_mag_logical(psfield, plfield)
rules_mag_functions(psfield, psfield)
rules_mag_functions(psfield, pvfield)
rules_mag_functions(psfield, ptfield)
rules_mag_functions(psfield, pyfield)
rules_mag_functions(psfield, phfield)

rule_vector_zip(pvfield, psfield, VECTOR)
rule_tensor_zip(ptfield, psfield, TENSOR)
rule_symTensor_zip(pyfield, psfield, SYM_TENSOR)
rule_sphTensor_zip(phfield, psfield, SPH_TENSOR)

rule_vector_components(psfield, pvfield)
rule_tensor_components(psfield, ptfield)
rule_symTensor_components(psfield, pyfield)
rule_sphTensor_components(psfield, phfield)

rule_tensor_transpose(ptfield)
rule_symTensor_transpose(pyfield)
rule_sphTensor_transpose(phfield)

rule_tensor_unzipDiag(pvfield, pyfield)
rule_tensor_unzipAll(pvfield, ptfield)

rule_faceToPoint(psfield, sfield)
rule_faceToPoint(pvfield, vfield)
rule_faceToPoint(ptfield, tfield)
rule_faceToPoint(pyfield, yfield)
rule_faceToPoint(phfield, hfield)


// ************************************************************************* //

/*
 * include m4/lemon/parser-methods.m4
include([m4/lemon/parser-methods.m4])dnl
dnl Can revert to standard m4 quoting
dnl ... Or not changequote([`],['])dnl Revert to standard m4 quoting
 */

%code
{

// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //

parser_code_static_methods(Foam::expressions::patchExpr::parser)


// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //

void Foam::expressions::patchExpr::parser::stop()
{
    if (lemon_)
    {
        ParseFree(lemon_, ::operator delete);
        #ifndef NDEBUG
        ParseTrace(nullptr, nullptr);
        #endif
        lemon_ = nullptr;
    }
}


void Foam::expressions::patchExpr::parser::start(parseDriver& driver_)
{
    this->stop();
    lemon_ = ParseAlloc(::operator new, &driver_);

    if ((debug & 0x4) || driver_.debugParser())
    {
        #ifndef NDEBUG
        ParseTrace(stderr, const_cast<char*>(prompt_));
        #endif
    }
}


void Foam::expressions::patchExpr::parser::parse
(
    int tokenId,
    scanToken* tokenVal
)
{
    Parse(lemon_, tokenId, tokenVal);
}


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

}  // End of %code

// ************************************************************************* //
